package viz.flutter.vutil

import androidx.annotation.NonNull

import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import android.provider.Settings
import android.util.Log
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding

import java.net.URLConnection
import java.io.File
import android.media.MediaScannerConnection
import java.io.IOException
import java.io.FileInputStream
import android.provider.MediaStore
import android.net.Uri
import android.content.ContentResolver
import android.content.ContentValues
import android.os.Build
import android.os.Environment
import android.os.FileUtils
import android.content.Intent
import android.os.Handler
import android.os.Looper
import java.io.FileNotFoundException

import java.io.FileOutputStream

import java.nio.channels.FileChannel
import java.util.*


/** VutilPlugin */
class VUtilPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private lateinit var channel : MethodChannel
  private var activityBinding: ActivityPluginBinding? = null
  private val TAG = "VUtilPlugin"
  private var copyTryTimes:Int = 0

  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "vutil")
    channel.setMethodCallHandler(this)
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) {
    when (call.method) {
      "getPlatformVersion" -> {
        result.success("Android ${android.os.Build.VERSION.RELEASE}")
      }
      "setDataToSettings" -> {
        val args = call.arguments as Map<String, Any>
        val key = args["key"] as String
        val value = args["value"] as String
        //设置系统配置文件中的数据，第一个参数固定的，但是需要上下文，第二个参数是保存的Key，第三个参数是保存的value
        val msave = Settings.System.putString(activityBinding!!.activity.getContentResolver(), key, value);//获取系统配置文件中的数据，第一个参数固定的，但是需要上下文，第二个参数是之前保存的Key，第三个参数表示如果没有这个key的情况的默认值
        result.success(msave)
      }
      "getDataToSettings" -> {
        val args = call.arguments as Map<String, Any>
        val key = args["key"] as String
        val defaultValue = args["defaultValue"] as String
        //设置系统配置文件中的数据，第一个参数固定的，但是需要上下文，第二个参数是保存的Key，第三个参数是保存的value
        val msave = Settings.System.getString(activityBinding!!.activity.getContentResolver(), key);//获取系统配置文件中的数据，第一个参数固定的，但是需要上下文，第二个参数是之前保存的Key，第三个参数表示如果没有这个key的情况的默认值
        result.success(msave ?: defaultValue)
      }
      "isRoot" -> {
        result.success(isRoot())
      }
      "saveToGallery" -> {
        val args = call.arguments as Map<String, Any>
        if(args["filePath"] == null) {
          result.success(false)
          return
        }
        var filePath = args["filePath"] as String
        var dir = Environment.DIRECTORY_DCIM
        if(args["dir"] != null) {
          dir = args["dir"] as String
        }
        if(filePath.isNotEmpty()) {
          scanFile(result, File(filePath), dir)
        }
      }
      "openApp" -> {
        try {
          val args = call.arguments as Map<String, Any>
          if (args["packageName"] == null || args["activityPath"] == null) {
            result.success(false)
            return
          }
          val packageName = args["packageName"] as String
          val activityPath = args["activityPath"] as String
          val it = Intent("android.intent.action.MAIN")
          it.setClassName(packageName, activityPath)
          activityBinding?.activity?.startActivity(it)
        } catch (e:Exception) {
          e.printStackTrace()
          result.success(false)
        }
      }
      else -> {
        result.notImplemented()
      }
    }
  }

  //判断机器是否Root
  fun isRoot(): Boolean {
    var bool = false
    try {
      bool = !(!File("/system/bin/su").exists() && !File("/system/xbin/su").exists())
    } catch (e: Exception) {
    }
    return bool
  }

  //保存图片
  fun scanFile(result: MethodChannel.Result, file: File, dir: String) {
    val mimeType = getMimeType(file)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
      val fileName = file.name
      val values = ContentValues()
      values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
      values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
      values.put(MediaStore.MediaColumns.RELATIVE_PATH, dir)
      val contentResolver: ContentResolver = activityBinding!!.activity!!.getContentResolver()
      val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
      if (uri == null) {
        result.success(false)
        return
      }
      try {
        val out = contentResolver.openOutputStream(uri)
        val fis = FileInputStream(file)
        FileUtils.copy(fis, out!!)
        fis.close()
        out.close()
        result.success(true)
      } catch (e: IOException) {
        e.printStackTrace()
        result.success(false)
      }
    } else {
      val sdcardPath = Environment.getExternalStorageDirectory().absolutePath
      val usedFilePath = "$sdcardPath/Pictures/${UUID.randomUUID()}.jpg"
      val usedFile = File(usedFilePath)
      copyTryTimes = 0
//      copyFile(file, usedFilePath)
      try {
//        FileUtilsCompatible.copyFile(file,File(usedFilePath))
          copyFile(file, usedFile)
      } catch (e: Exception) {
        e.printStackTrace()
        result.success(false)
        return
      }
      MediaScannerConnection.scanFile(activityBinding!!.activity!!, arrayOf(usedFilePath), arrayOf(mimeType)) { path: String, uri: Uri? ->
        run {
          Log.i(TAG, path)
          uri?.apply {
            Log.i(TAG, this.toString())
          }
          Handler(Looper.getMainLooper()).post { result.success(true) }
        }
      }
    }
  }

  fun copyFile(src: File?,dest: File?):Boolean {
    org.apache.commons.io.FileUtils.copyFile(src, dest)
    val fileSize = src!!.length()
    val fileNewSize = dest!!.length()
    Log.i(TAG, "$fileSize $fileNewSize")
    if(fileNewSize != fileSize || fileSize == 667L) {
      Log.i(TAG, "第${copyTryTimes}次尝试重新copy")
      if(copyTryTimes == 10) {
        Log.i(TAG, "尝试重新copy 10次均失败")
        return false
      }
      copyTryTimes++
      return copyFile(src, dest)
    }
    return true
  }

  /** 根据文件路径拷贝文件
   * @param src 源文件
   * @param destPath 目标文件路径
   * @return boolean 成功true、失败false
   */
  fun copyFile(src: File?, destPath: String?): Boolean {
    var result = false
    if (src == null || destPath == null) {
      return result
    }
    val dest = File(destPath)
    if (dest != null && dest.exists()) {
      dest.delete() // delete file
    }
    try {
      dest.createNewFile()
    } catch (e: IOException) {
      e.printStackTrace()
    }
    var srcChannel: FileChannel? = null
    var dstChannel: FileChannel? = null
    try {
      srcChannel = FileInputStream(src).channel
      dstChannel = FileOutputStream(dest).channel
      srcChannel.transferTo(0, srcChannel.size(), dstChannel)
      result = true
      val fileSize = srcChannel.size()
      val fileNewSize = dstChannel.size()
      Log.i(TAG, "$fileSize $fileNewSize")
      if(fileNewSize != fileSize || fileSize == 667L) {
        try {
          srcChannel.close()
          dstChannel.close()
        } catch (e: IOException) {
          e.printStackTrace()
        }
        if(copyTryTimes == 3) {
          Log.i(TAG, "尝试重新copy3次均失败")
          return false
        }
        copyTryTimes++
        return copyFile(src,destPath)
      }
    } catch (e: FileNotFoundException) {
      e.printStackTrace()
      return result
    } catch (e: IOException) {
      e.printStackTrace()
      return result
    }
    try {
      srcChannel.close()
      dstChannel.close()
    } catch (e: IOException) {
      e.printStackTrace()
    }
    return result
  }

  /**
   * 复制单个文件
   *
   * @param oldPathName String 原文件路径+文件名 如：data/user/0/com.test/files/abc.txt
   * @param newPathName String 复制后路径+文件名 如：data/user/0/com.test/cache/abc.txt
   * @return `true` if and only if the file was copied;
   * `false` otherwise
   */
  fun copyFile(oldPathName: String?, newPathName: String?): Boolean {
    return try {
      val oldFile = File(oldPathName)
      if (!oldFile.exists()) {
        Log.e("--Method--", "copyFile:  oldFile not exist.")
        return false
      } else if (!oldFile.isFile) {
        Log.e("--Method--", "copyFile:  oldFile not file.")
        return false
      } else if (!oldFile.canRead()) {
        Log.e("--Method--", "copyFile:  oldFile cannot read.")
        return false
      }

      /* 如果不需要打log，可以使用下面的语句
if (!oldFile.exists() || !oldFile.isFile() || !oldFile.canRead()) {
    return false;
}
*/
      val fileInputStream = FileInputStream(oldPathName)
      val fileOutputStream = FileOutputStream(newPathName)
      val buffer = ByteArray(1024)
      var byteRead: Int
      while (-1 != fileInputStream.read(buffer).also { byteRead = it }) {
        fileOutputStream.write(buffer, 0, byteRead)
      }
      fileInputStream.close()
      fileOutputStream.flush()
      fileOutputStream.close()
      true
    } catch (e: Exception) {
      e.printStackTrace()
      false
    }
  }

  fun getMimeType(file: File): String {
    val fileNameMap = URLConnection.getFileNameMap()
    return fileNameMap.getContentTypeFor(file.name)
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }

  override fun onAttachedToActivity(binding: ActivityPluginBinding) {
    activityBinding = binding
  }

  override fun onDetachedFromActivityForConfigChanges() {
  }

  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
  }

  override fun onDetachedFromActivity() {
    activityBinding = null
  }
}
